#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "Hello.h"
#include <iostream>

using namespace testing;

struct TestClass {
        int testMember;
    public:
        TestClass() { std::cout << "creation\n"; }
        ~TestClass() { std::cout << "destruction\n"; }
        TestClass(TestClass&&) { std::cout << "move\n"; }
        TestClass( const TestClass &other){
            this->testMember = other.testMember;
            std::cout << "copy\n"; 
        }// 拷贝构造函数
};

TestClass f2()
{
    std::cout << "before creation\n";
    TestClass result = TestClass{};
    result.testMember = 11;
    return result;
};

TestClass& f1()
{
    TestClass result = TestClass{};
    result.testMember = 11;
    //在这里拷贝就是正确的
    // std::cout << result.testMember << "\n"; 
    // TestClass t = result;
    // std::cout << t.testMember << "\n"; 
    return result; //这种写法是会被warning的，意味着可能存在风险
};

TestClass f()
{
     TestClass result = TestClass{};
     result.testMember = 11;
     return result;
};

TEST(ScopeTest, right_value_reference_example)
{
    std::cout << "before f call\n";
    TestClass && t = f();// 这是一个右值引用，这个写法有点问题，他把本来是返回一个值的函数变成了返回引用，这大概就是右值引用的意思了
    std::cout << "after f call\n";
    std::cout << t.testMember << "\n"; //这里是对的
    //clang 会调用移动构造，gcc 则不会，这也太坑了。
    //clang样例代码: https://rextester.com/DCF22988
    //这事比我想象的坑，在mac下的clang不会调，上面按个在线的会……
}

TEST(ScopeTest, return_reference_example)
{
    std::cout << "before f1 call-reference\n";
    const TestClass &t = f1(); //在里面已经被析构了，那出来用会怎么样？
    std::cout << "after f1 call\n";
    // std::cout << t.testMember << "\n"; //因为里面已经析构了，所以这个数据已经是错误的了
    //在gcc里上面这行会segmentfault

}

TEST(ScopeTest, return_reference_example2)
{
    std::cout << "before f1 call 2-value\n";
    // TestClass t = f1(); //会在函数内调用析构，但是出来后又调用了一次析构,本行赋值语句其实是调用了拷贝构造
    //因为是先析构后copy，所以属性值已经乱了。
    //在gcc里上面这行会segmentfault，clang就没问题……我不知道是配置参数的差别还是什么
    std::cout << "after f1 call 2\n";
    // std::cout << t.testMember << "\n"; 

}


TEST(ScopeTest, return_reference_example3)
{
    std::cout << "before f2 call-reference\n";
    //gcc里下面这行也可以工作……C++的编译器真的是……
    const TestClass &t = f2(); //引用就不会被展开,但是如果不加const，竟然会出错
    
    std::cout << "after f2 call\n";
    std::cout << t.testMember << "\n"; 

}

TEST(ScopeTest, return_reference_example4)
{
    std::cout << "before f2 call-value\n";
    TestClass t = f2(); //值却会被展开
    std::cout << "after f2 call\n";
    std::cout << t.testMember << "\n"; //这里有时正确，有时错误。只在开始的时候是乱的，但是后面都是对的了，很奇怪。
    // 所以正确的做法就是这一个
}